Grid.prototype = []; // Inherit from Array
Grid.prototype.constructor = Grid; // Set the "class" back to Grid

Grid.prototype.copy = function () {
    let n = new Grid(this.width, this.height);
    let x, y;
    for (x = 0; x < this.width; ++x) {
        for (y = 0; y < this.height; ++y) {
            n[x][y] = this[x][y];
        }
    }
    return n;
};

Grid.prototype.size = function () {
    return "(" + this.width + ", " + this.height + ")";
};

Grid.prototype.setWidth = function (x) {
    if (x < this.width) {
        this.length = x;
    }
    if (x > this.width) {
        let i;
        for (i = this.width; i < x; ++i) {
            this[i] = new Array(this.height);
        }
    }

    this.width = x;
};

Grid.prototype.setHeight = function (y) {
    if (y < this.height) {
        let i;
        for (i = 0; i < this.width; ++i) {
            this[i].length = y;
        }
    }
    this.height = y;
};

Grid.prototype.reset = function(){
    this.length = 0;
    let i = 0;
    for(i;i<this.width;++i){
        this[i].length = 0;
    }
}

Grid.prototype.isSame = function(grid){
    return grid.size() == this.size();
};

Grid.prototype.isSameCell = function(x, y, grid){
    if(x >= this.width || y >= this.height || x < 0 || y < 0){
        return true;
    }
    if(this[x][y] == undefined && grid[x][y] == undefined){
        return true;
    }
    if(this[x][y] != grid[x][y]){
        return false;
    }
    return this[x][y].color == grid[x][y].color;
};

function Grid(width, height) {
    if (isNaN(Number(width)) ||
        isNaN(Number(height))) {
        throw new Error(
            "Grid constructor requires two numerical dimensions");
    }

    this.width  = width;
    this.height = height;

    this.length = width;
    let x;
    for (x = 0; x < width; ++x) {
        this[x] = new Array(height);
    }
}

Model.deSerialize = function(data){
    let m, d;
    if (protoSupported()) {
        m = JSON.parse(data);
        m.__proto__ = Model.prototype;
        m.d_grid.__proto__ = Grid.prototype;
    } else {
        d = JSON.parse(data);
        m = new Model();
        m.d_grid = new Grid(d.d_grid.width, d.d_grid.height);

        for (p in d.d_grid) {
            m.d_grid[p] = d.d_grid[p];
        }
        for (p in d) {
            if (p !== 'd_grid') {
                m[p] = d[p];
            }
        }
        m.__proto__ = Model.prototype;
        m.d_grid.__proto__ = Grid.prototype;
    }
    return m;
}

Model.serialize = function (model) {
    return JSON.stringify(model);
};

Model.prototype.grid = function (grid) {
    if(grid != undefined){
        this.d_grid = grid;
    }
    return this.d_grid;
};

Model.prototype.reset = function(){
    this.d_grid.reset();

}

Model.prototype.cell = function (x, y, key, value) {
    let ret = undefined;
    if (!(x >= 0 && y >= 0)) {
        throw new Error("Model.point: must specify x and y coords");
    }
    if (value !== undefined) {
        if(this.d_grid[x][y] == undefined){
            this.d_grid[x][y] = {};
        }
        this.d_grid[x][y][key] = value;
    }else if(key !== undefined && this.d_grid[x][y] != undefined){
        ret = this.d_grid[x][y][key];
    }else if(key == undefined){
        ret = this.d_grid[x][y];
    }
    return ret;
};

Model.prototype.loadConfig = function(config){
    this.config = {};
    this.config.cellSize = config.cell_size;
};

Model.prototype.setSize = function (w,h) {
    let width = Math.ceil(w/this.config.cellSize);
    let height = Math.ceil(h/this.config.cellSize);
    if(this.d_grid == undefined){
        this.d_grid = new Grid(width, height);
    }else{
        this.d_grid.setWidth(width);
        this.d_grid.setHeight(height);
    }
};

function Model(config) {
    this.loadConfig(config);
}